package com.nervenets.utils;

import org.joda.time.LocalDate;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author Joe create 2009-1-2 13:20:16
 * @link {http://www.nervenets.com}
 * @copyright YunKing Union
 */
public final class ValidatorUtils {
    private static final String EMAIL_REGULAR_EXPRESSION = "^[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$";

    private static final int MINIMUM_PHONE_LENGTH = 1;
    private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    private static final char[] ALLOWED_PHONE_CHARACTERS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '(', ')', ' ', '-'};
    public static final String HTTP_START_MATCH = "^(http|https|ftp|file)://";
    public static final String DOMAIN_MATCH = "([0-9a-z][0-9a-z-]*\\.)*[a-z]{2,6}";
    public static final String IP_MATCH = "((25[0-5]|2[0-4]\\d|1\\d\\d|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d\\d|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d\\d|\\d{1,2})\\.(25[0-5]|2[0-4]\\d|1\\d\\d|\\d{1,2}))";
    public static final String PARAMS_MATCH = "(/[\\w!~*'()\\.;?:@&=+$,%#-[\u4e00-\u9fa5]]*)+/?$";

    public static boolean isValidName(String name) {
        Pattern pattern = Pattern.compile("[a-zA-Z0-9]*");
        Matcher matcher = pattern.matcher(name);
        return !isEmptyContent(name) && matcher.matches();
    }

    public static boolean isValidPhoneOrFax(String phoneOrFax) {
        if (phoneOrFax == null || phoneOrFax.length() < MINIMUM_PHONE_LENGTH) {
            return false;
        }
        int length = phoneOrFax.length();
        for (int i = 0; i < length; i++) {
            char character = phoneOrFax.charAt(i);
            boolean isValidCharacter = false;
            for (char allowedCharacter : ALLOWED_PHONE_CHARACTERS) {
                if (character == allowedCharacter) {
                    isValidCharacter = true;
                    break;
                }
            }
            if (!isValidCharacter) {
                return false;
            }
        }
        return true;
    }

    /**
     * Uses {@link #EMAIL_REGULAR_EXPRESSION} to validate the
     * email address.
     *
     * @param emailAddress The email address to validate.
     * @return True if it's a valid email address (according to the mail specification)
     */
    public static boolean isValidEmail(String emailAddress) {
        if (!StringUtils.hasText(emailAddress)) {
            return false;
        }

        Pattern p = Pattern.compile(EMAIL_REGULAR_EXPRESSION);
        Matcher m = p.matcher(emailAddress);
        return m.matches();
    }

    /**
     * To judge a String object contains absolute numbers.
     *
     * @param str The String object which we want to judge
     * @return <code>true</code> if str contains absolute number characters,otherwise <code>false</code>
     */
    public static boolean isNumeric(String str) {
        if (isEmptyContent(str)) return false;
        String trimedStr = StringUtils.trimAllWhitespace(str);
        if (trimedStr.length() == 0) return false;
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(trimedStr);
        return isNum.matches();
    }

    public static boolean isBirthdateBeforeToday(LocalDate date) {
        return date.isBefore(JodaUtils.today());
    }

    public static boolean isEmptyContent(String content) {
        return !(StringUtils.hasText(content));
    }

    public static boolean isValidPassword(String password, int length) {
        return !isEmptyContent(password) && password.length() >= length;
    }

    public static boolean isValidDate(String dateAsString) {
        try {
            final Date date = new SimpleDateFormat("yyyy-MM-dd").parse(dateAsString);
            System.out.println(date);
            return true;
        } catch (ParseException e) {
            return false;
        }
    }


    /**
     * To judge a String object contains absolute float .
     *
     * @param str The String object which we want to judge
     * @return <code>true</code> if str contains absolute number characters,otherwise <code>false</code>
     */
    public static boolean isFloatNumeric(String str) {
        if (isEmptyContent(str)) return false;
        String trimedStr = StringUtils.trimAllWhitespace(str);
        if (trimedStr.length() == 0) return false;
        Pattern pattern = Pattern.compile("^-?(0|\\d+)(\\.\\d+)?$");
        Matcher isNum = pattern.matcher(trimedStr);
        return isNum.matches();
    }

    public static boolean isMoney(String money) {
        money = money.replace(".", "");
        return isNumeric(money);
    }

    public static boolean isAfterToday(LocalDate date) {
        return date.isAfter(JodaUtils.today());
    }

    public static boolean isAfterTodayIncludingToday(LocalDate date) {
        return !date.isBefore(JodaUtils.today());
    }

    /**
     * To judge a String object contains absolute letter.
     *
     * @param str The String object which we want to judge
     * @return <code>true</true> if str contains absolute letter characters,otherwise <code>false</code>
     */
    public static boolean isLetter(String str) {
        String testedStr = StringUtils.trimAllWhitespace(str.toLowerCase());
        if (testedStr.length() == 0) return false;
        Pattern pattern = Pattern.compile("[a-z]*");
        Matcher isLetter = pattern.matcher(testedStr);
        return isLetter.matches();
    }

    public static boolean startLetter(String str) {
        String testedStr = StringUtils.trimAllWhitespace(str.toLowerCase());
        if (testedStr.length() == 0) return false;
        Pattern pattern = Pattern.compile("^[a-z]*");
        Matcher isLetter = pattern.matcher(testedStr);
        return isLetter.matches();
    }

    public static boolean isValidZipCode(String standard, String zipCode) {
        int firstNumber = Integer.valueOf(standard.substring(0, 1));
        String firstCharcher = standard.substring(1, 2);
        int secondNumber = Integer.valueOf(standard.substring(2, 3));
        String secondCharcter = standard.substring(3, 4);

        boolean validZipcode = false;
        if (((firstNumber + secondNumber) == zipCode.length())) {
            if ("N".equals(firstCharcher) && "C".equals(secondCharcter)) {
                if (firstNumber != 0) {
                    validZipcode = isNumeric(zipCode.substring(0, firstNumber));
                }
                if (secondNumber != 0) {
                    validZipcode = isLetter(zipCode.substring(firstNumber, firstNumber + secondNumber));
                }

            } else if ("N".equals(firstCharcher) && "N".equals(secondCharcter)) {
                if (firstNumber != 0) {
                    validZipcode = isNumeric(zipCode.substring(0, firstNumber));
                }
                if (secondNumber != 0) {
                    validZipcode = isNumeric(zipCode.substring(firstNumber, firstNumber + secondNumber));
                }
            } else if ("C".equals(firstCharcher) && "N".equals(secondCharcter)) {
                if (firstNumber != 0) {
                    validZipcode = isLetter(zipCode.substring(0, firstNumber));
                }
                if (secondNumber != 0) {
                    validZipcode = isNumeric(zipCode.substring(firstNumber, firstNumber + secondNumber));
                }
            } else if ("C".equals(firstCharcher) && "C".equals(secondCharcter)) {
                if (firstNumber != 0) {
                    validZipcode = isLetter(zipCode.substring(0, firstNumber));
                }
                if (secondNumber != 0) {
                    validZipcode = isLetter(zipCode.substring(firstNumber, firstNumber + secondNumber));
                }
            }
        }
        return validZipcode;
    }

    public static boolean isUrl(String url) {
        String httpRegex = "((http|ftp|https)://)((([a-zA-Z0-9\\._-]+\\.[a-zA-Z]{2,6})|([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}))|(localhost))(:[0-9]{1,4})*(/[a-zA-Z0-9\\&%_\\./-~-]*)?";
        Pattern pattern = Pattern.compile(httpRegex, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(url);
        return matcher.find();
    }

    public static boolean isMobile(String mobile) {
        return mobile.matches("^0{0,1}(13[0-9]|15[0-9]|17[0-9]|18[0-9]|19[0-9])[0-9]{8}$");
    }

    public static boolean isDomain(String domain) {
        if (domain.startsWith("www.")) {
            return false;
        }
        return Pattern.matches(DOMAIN_MATCH, domain);
    }

    public static boolean isValidCoordinate(double longitude, double latitude) {
        return !(0.0 > longitude) && !(180.0 < longitude) && !(0.0 > latitude) && !(90.0 < latitude);
    }

    public static boolean isValidChinaCoordinate(double longitude, double latitude) {
        return !(73.0 > longitude) && !(135.0 < longitude) && !(17.0 > latitude) && !(53.0 < latitude);
    }
}
